/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.revivius.jbtorrent;

import com.revivius.jbtorrent.entity.Torrent;
import java.io.IOException;
import java.math.BigInteger;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Revivius
 */
public class UDPTrackerTest {

    public static final int ACTION_CONNECT = 0;
    public static final int ACTION_ANNOUNCE = 1;
    public static final int ACTION_SCRAPE = 2;
    // server only
    public static final int ACTION_ERROR = 3;
    
    public static final int EVENT_NONE = 0;
    public static final int EVENT_COMPLETED = 1;
    public static final int ACTION_STARTED = 2;
    public static final int ACTION_STOPPED = 3;
    
    
    private static Torrent torrent;
    
    public static InetAddress address;
    public static int port;
    
    private static AtomicInteger transactionIdCounter = new AtomicInteger(0);

    private static long connectionId;

    /**
     * 1- Connect to tracker to obtain a connection id
     * 2- Announce our existence and retrieve list of peers
     * 3- TODO: implement TCP peer protocol (google for BitTorrent Peer Protocol)
     * @param args 
     */
    
    public static void main(String[] args) {

        try {
            torrent = ParserTest.getTorrent();
            
            address = InetAddress.getByName("tracker.publicbt.com");
            port = 80;

            DatagramSocket socket = new DatagramSocket();
            doConnection(socket);
            doAnnounce(socket);
            
        } catch (Exception ex) {
            Logger.getLogger(UDPTrackerTest.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    private static void doConnection(DatagramSocket socket) throws Exception {
        
        System.out.println("------ CONNECT -------");
        
        ByteBuffer ob = ByteBuffer.allocate(16);
        // connection id
        ob.putLong(0x41727101980L);
        // connect action
        ob.putInt(ACTION_CONNECT);
        // transaction id
        ob.putInt(transactionIdCounter.incrementAndGet());
        
        sendPacket(socket, ob);

        ByteBuffer ib = receivePacket(socket);

        int action = ib.getInt();
        System.out.println("Action: " + action);
        int transactionId = ib.getInt();
        System.out.println("Transaction Id: " + transactionId);
        
        connectionId = ib.getLong();
        System.out.println("Connection Id: " + connectionId);
    }

    public static void doAnnounce(DatagramSocket socket) throws Exception {
        System.out.println("------ ANNOUNCE -------");
        
        ByteBuffer ob = ByteBuffer.allocate(100);
        
        // connection id
        ob.putLong(connectionId);
        // announce action
        ob.putInt(ACTION_ANNOUNCE);
        // transaction id
        ob.putInt(transactionIdCounter.incrementAndGet());
        // info hash
        ob.put(torrent.getInfo().getHash());
        // peer id
        byte[] peerId = new byte[20];
        Arrays.fill(peerId, (byte) 1);
        ob.put(peerId);
        // downloaded
        ob.putLong(0L);
        // left
        ob.putLong(4194304L);
        // uploaded
        ob.putLong(0L);
        // event NONE
        ob.putInt(EVENT_NONE);
        // ip
        ob.putInt(0);
        // key
        ob.putInt(10);
        // num want - The maximum number of peers you want
        ob.putInt(-1);
        // port
        ob.putShort((short) 6882);
        
        sendPacket(socket, ob);
        
        ByteBuffer ib = receivePacket(socket);
        
        int action = ib.getInt();
        System.out.println("Action: " + action);
        int transactionId = ib.getInt();
        System.out.println("Transaction Id: " + transactionId);
        int interval = ib.getInt();
        System.out.println("Interval: " + interval);
        int leechers = ib.getInt();
        System.out.println("Leechers: " + leechers);
        int seeders = ib.getInt();
        System.out.println("Seeders: " + seeders);
        
        
        int peersAvailable = (ib.limit() - 20) / 6;
        
        System.out.println("Peers (" + peersAvailable + "):");
        for (int i = 0; i < peersAvailable; i++) {
            int peerIp = ib.getInt();
            int peerPort = ib.getChar();
            
            
            
            byte[] bytes = BigInteger.valueOf(peerIp).toByteArray();
            InetAddress peerAddr = InetAddress.getByAddress(bytes);
            System.out.println(i + ") " + peerAddr + ":" + peerPort);
        }
        
    }
    
    private static void sendPacket(DatagramSocket socket, ByteBuffer buffer) throws IOException {
        byte[] data = buffer.array();
        DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
        socket.send(packet);
    }
    
    private static ByteBuffer receivePacket(DatagramSocket socket) throws IOException {
        byte[] data = new byte[8096];
        DatagramPacket packet = new DatagramPacket(data, data.length);
        socket.receive(packet);
        
        System.out.println("Received " + packet.getLength() + " bytes");
        
        ByteBuffer wrap = ByteBuffer.wrap(packet.getData(), 0, packet.getLength());
        return wrap;
    }

}
